package com.jl.filter;

import com.jl.JLReflect;
import com.jl.JLTuple;
import com.jl.filter.annotate.JLFilter;
import com.jl.filter.error.JLFilterError;
import com.jl.set.list.JArrayList;
import com.jl.set.list.JList;
import lombok.Builder;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 过滤器
 */
@Slf4j
@Component
public class JLFilterImpl implements Filter {

    @Autowired
    private ApplicationContext applicationContext;

    private JList<FilterParam> list;

    @Data
    @Builder
    public static class FilterParam {
        private Object bean;

        private Method method;

        private int order;

        private JLFilter jlFilter;
    }

    /**
     * 初始化
     */
    @Override
    public void init(FilterConfig filterConfig) {
        //缓存
        JList<FilterParam> jList = new JArrayList<>();
        String[] beans = applicationContext.getBeanDefinitionNames();
        for (String bean : beans) {
            Object beanObj;
            try {
                beanObj = applicationContext.getBean(bean);
            } catch (Exception e) {
                continue;
            }
            List<JLTuple.Tuple2<Method, JLFilter>> methods = JLReflect.MethodReflect.getMethod(beanObj.getClass(), JLFilter.class);
            for (JLTuple.Tuple2<Method, JLFilter> methodObj : methods) {
                Method method = methodObj.getV1();
                JLFilter jlFilter = methodObj.getV2();
                int order = jlFilter.order();
                FilterParam filterParam = FilterParam.builder()
                        .bean(beanObj)
                        .method(method)
                        .order(order)
                        .jlFilter(jlFilter)
                        .build();
                jList.add(filterParam);
            }
        }
        list = jList.asc(FilterParam::getOrder);
    }

    /**
     * 执行
     */
    @Override
    @SneakyThrows
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        for (FilterParam filterParam : list) {
            JLFilter jlFilter = filterParam.getJlFilter();
            String[] excludes = jlFilter.excludes();
            List<String> excludesList = Arrays.asList(excludes);
            if (excludesList.contains(request.getRequestURI())) {
                continue;
            }
            Object bean = filterParam.getBean();
            Method method = filterParam.getMethod();
            Object[] value = jlFilter.values();
            String error = jlFilter.error();
            Object[] param = JLReflect.MethodReflect.checkParamType(method.getParameters(), value);
            Object invoke = null;
            try {
                invoke = method.invoke(bean, param);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (invoke != null && invoke instanceof Boolean) {
                if (!Boolean.parseBoolean(invoke.toString())) {
                    throw new JLFilterError(error);
                }
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    /**
     * 销毁
     */
    @Override
    public void destroy() {

    }

    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(this);
        registration.addUrlPatterns("/*");
        registration.setName("jlFilter");
        registration.setOrder(0);
        return registration;
    }
}
